home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / nos042_s / slhc.c < prev    next >
C/C++ Source or Header  |  1994-09-16  |  16KB  |  591 lines

  1. /*
  2.  * Routines to compress and uncompress tcp packets (for transmission
  3.  * over low speed serial lines).
  4.  *
  5.  * Copyright (c) 1989 Regents of the University of California.
  6.  * All rights reserved.
  7.  *
  8.  * Redistribution and use in source and binary forms are permitted
  9.  * provided that the above copyright notice and this paragraph are
  10.  * duplicated in all such forms and that any documentation,
  11.  * advertising materials, and other materials related to such
  12.  * distribution and use acknowledge that the software was developed
  13.  * by the University of California, Berkeley.  The name of the
  14.  * University may not be used to endorse or promote products derived
  15.  * from this software without specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  17.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  18.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  *
  20.  *    Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
  21.  *    - Initial distribution.
  22.  *
  23.  *
  24.  * modified for KA9Q Internet Software Package by
  25.  * Katie Stevens (dkstevens@ucdavis.edu)
  26.  * University of California, Davis
  27.  * Computing Services
  28.  *    - 01-31-90    initial adaptation (from 1.19)
  29.  *    PPP.05    02-15-90 [ks]
  30.  *    PPP.08    05-02-90 [ks]    use PPP protocol field to signal compression
  31.  *    PPP.15    09-90     [ks]    improve mbuf handling
  32.  *    PPP.16    11-02     [karn]    substantially rewritten to use NOS facilities
  33.  *
  34.  *    - Feb 1991    Bill_Simpson@um.cc.umich.edu
  35.  *            variable number of conversation slots
  36.  *            allow zero or one slots
  37.  *            separate routines
  38.  *            status display
  39.  *
  40.  *  ATARI Version by David Nash - dnash@chaos.demon.co.uk
  41.  *
  42.  *  cond out mem.h, ANSI function definition for encode
  43.  */
  44.  
  45. /****************************************************************************
  46. *    $Id: slhc.c 1.2 93/07/16 11:50:16 ROOT_DOS Exp $
  47. *    14 Jul 93    1.2        GT    Fix warnings.                                    *
  48. ****************************************************************************/
  49.  
  50. #ifndef ATARI
  51. #include <mem.h>
  52. #endif
  53.  
  54. #include "global.h"
  55. #include "mbuf.h"
  56. #include "internet.h"
  57. #include "ip.h"
  58. #include "tcp.h"
  59. #include "slhc.h"
  60.  
  61. static char *encode __ARGS((char *cp,int16 n));
  62. static long decode __ARGS((struct mbuf **bpp));
  63.  
  64.  
  65. /* Initialize compression data structure
  66.  *    slots must be in range 0 to 255 (zero meaning no compression)
  67.  */
  68. struct slcompress *
  69. slhc_init( rslots, tslots )
  70. int rslots;
  71. int tslots;
  72. {
  73.     register int16 i;
  74.     register struct cstate *ts;
  75.     struct slcompress *comp;
  76.  
  77.     comp = callocw( 1, sizeof(struct slcompress) );
  78.  
  79.     if ( rslots > 0  &&  rslots < 256 ) {
  80.         comp->rstate = callocw( rslots, sizeof(struct cstate) );
  81.         comp->rslot_limit = rslots - 1;
  82.     }
  83.  
  84.     if ( tslots > 0  &&  tslots < 256 ) {
  85.         comp->tstate = callocw( tslots, sizeof(struct cstate) );
  86.         comp->tslot_limit = tslots - 1;
  87.     }
  88.  
  89.     comp->xmit_oldest = 0;
  90.     comp->xmit_current = 255;
  91.     comp->recv_current = 255;
  92.  
  93.     if ( tslots > 0 ) {
  94.         ts = comp->tstate;
  95.         for(i = comp->tslot_limit; i > 0; --i){
  96.             ts[i].this = i;
  97.             ts[i].next = &(ts[i - 1]);
  98.         }
  99.         ts[0].next = &(ts[comp->tslot_limit]);
  100.         ts[0].this = 0;
  101.     }
  102.     return comp;
  103. }
  104.  
  105.  
  106. /* Free a compression data structure */
  107. void
  108. slhc_free(comp)
  109. struct slcompress *comp;
  110. {
  111.     if ( comp == NULLSLCOMPR )
  112.         return;
  113.  
  114.     if ( comp->rstate != NULLSLSTATE )
  115.         free( comp->rstate );
  116.  
  117.     if ( comp->tstate != NULLSLSTATE )
  118.         free( comp->tstate );
  119.  
  120.     free( comp );
  121. }
  122.  
  123.  
  124. /* Encode a number */
  125.  
  126. static char *encode(
  127.     char *cp,
  128.     int16 n)
  129. {
  130.     if(n >= 256 || n == 0){
  131.         *cp++ = 0;
  132.         cp = put16(cp,n);
  133.     } else {
  134.         *cp++ = n;
  135.     }
  136.     return cp;
  137. }
  138.  
  139. /* Decode a number */
  140. static long
  141. decode(bpp)
  142. struct mbuf **bpp;
  143. {
  144.     register int x;
  145.  
  146.     x = PULLCHAR(bpp);
  147.     if(x == 0){
  148.         return pull16(bpp);    /* pull16 returns -1 on error */
  149.     } else {
  150.         return (long)x;        /* -1 if PULLCHAR returned error */
  151.     }
  152. }
  153.  
  154. int
  155. slhc_compress(comp, bpp, compress_cid)
  156. struct slcompress *comp;
  157. struct mbuf **bpp;
  158. int compress_cid;
  159. {
  160.     register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
  161.     register struct cstate *lcs = ocs;
  162.     register struct cstate *cs = lcs->next;
  163.     register int16 hlen;
  164.     register struct tcp *oth;
  165.     register unsigned long deltaS, deltaA;
  166.     register int16 changes = 0;
  167.     char new_seq[16];
  168.     register char *cp = new_seq;
  169.     struct mbuf *bp;
  170.     struct tcp th;
  171.     struct ip iph;
  172.  
  173.     /* Extract IP header */
  174.     hlen = ntohip(&iph,bpp);
  175.  
  176.     /* Bail if this packet isn't TCP, or is an IP fragment */
  177.     if(iph.protocol != TCP_PTCL || iph.offset != 0 || iph.flags.mf){
  178.         /* Send as regular IP */
  179.         if(iph.protocol != TCP_PTCL)
  180.             comp->sls_o_nontcp++;
  181.         else
  182.             comp->sls_o_tcp++;
  183.         *bpp = htonip(&iph,*bpp,IP_CS_OLD);
  184.         return SL_TYPE_IP;
  185.     }
  186.     /* Extract TCP header */
  187.     hlen += ntohtcp(&th,bpp);
  188.  
  189.     /*  Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
  190.      *  some other control bit is set).
  191.      */
  192.     if(th.flags.syn || th.flags.fin || th.flags.rst || !th.flags.ack){
  193.         /* TCP connection stuff; send as regular IP */
  194.         comp->sls_o_tcp++;
  195.         *bpp = htontcp(&th,*bpp,NULLHEADER);
  196.         *bpp = htonip(&iph,*bpp,IP_CS_OLD);
  197.         return SL_TYPE_IP;
  198.     }
  199.     /*
  200.      * Packet is compressible -- we're going to send either a
  201.      * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way,
  202.      * we need to locate (or create) the connection state.
  203.      *
  204.      * States are kept in a circularly linked list with
  205.      * xmit_oldest pointing to the end of the list.  The
  206.      * list is kept in lru order by moving a state to the
  207.      * head of the list whenever it is referenced.  Since
  208.      * the list is short and, empirically, the connection
  209.      * we want is almost always near the front, we locate
  210.      * states via linear search.  If we don't find a state
  211.      * for the datagram, the oldest state is (re-)used.
  212.      */
  213.     for ( ; ; ) {
  214.         if( iph.source == cs->cs_ip.source
  215.          && iph.dest == cs->cs_ip.dest
  216.          && th.source == cs->cs_tcp.source
  217.          && th.dest == cs->cs_tcp.dest)
  218.             goto found;
  219.  
  220.         /* if current equal oldest, at end of list */
  221.         if ( cs == ocs )
  222.             break;
  223.         lcs = cs;
  224.         cs = cs->next;
  225.         comp->sls_o_searches++;
  226.     };
  227.     /*
  228.      * Didn't find it -- re-use oldest cstate.  Send an
  229.      * uncompressed packet that tells the other side what
  230.      * connection number we're using for this conversation.
  231.      *
  232.      * Note that since the state list is circular, the oldest
  233.      * state points to the newest and we only need to set
  234.      * xmit_oldest to update the lru linkage.
  235.      */
  236.     comp->sls_o_misses++;
  237.     comp->xmit_oldest = lcs->this;
  238.  
  239.     goto uncompressed;
  240.  
  241. found:
  242.     /*
  243.      * Found it -- move to the front on the connection list.
  244.      */
  245.     if(lcs == ocs) {
  246.         /* found at most recently used */
  247.     } else if (cs == ocs) {
  248.         /* found at least recently used */
  249.         comp->xmit_oldest = lcs->this;
  250.     } else {
  251.         /* more than 2 elements */
  252.         lcs->next = cs->next;
  253.         cs->next = ocs->next;
  254.         ocs->next = cs;
  255.     }
  256.  
  257.     /*
  258.      * Make sure that only what we expect to change changed.
  259.      * Check the following:
  260.      * IP protocol version, header length & type of service.
  261.      * The "Don't fragment" bit.
  262.      * The time-to-live field.
  263.      * The TCP header length.
  264.      * IP options, if any.
  265.      * TCP options, if any.
  266.      * If any of these things are different between the previous &
  267.      * current datagram, we send the current datagram `uncompressed'.
  268.      */
  269.     oth = &cs->cs_tcp;
  270.  
  271.     if(iph.version != cs->cs_ip.version || iph.optlen != cs->cs_ip.optlen
  272.      || iph.tos != cs->cs_ip.tos
  273.      || iph.flags.df != cs->cs_ip.flags.df
  274.      || iph.ttl != cs->cs_ip.ttl
  275.      || th.optlen != cs->cs_tcp.optlen
  276.      || (iph.optlen > 0 && memcmp(iph.options,cs->cs_ip.options,iph.optlen) != 0)
  277.      || (th.optlen > 0 && memcmp(th.options,cs->cs_tcp.options,th.optlen) != 0)){
  278.         goto uncompressed;
  279.     }
  280.     /*
  281.      * Figure out which of the changing fields changed.  The
  282.      * receiver expects changes in the order: urgent, window,
  283.      * ack, seq (the order minimizes the number of temporaries
  284.      * needed in this section of code).
  285.      */
  286.     if(th.flags.urg){
  287.         deltaS = th.up;
  288.         cp = encode(cp,deltaS);
  289.         changes |= NEW_U;
  290.     } else if(th.up != oth->up){
  291.         /* argh! URG not set but urp changed -- a sensible
  292.          * implementation should never do this but RFC793
  293.          * doesn't prohibit the change so we have to deal
  294.          * with it. */
  295.         goto uncompressed;
  296.     }
  297.     if((deltaS = th.wnd - oth->wnd) != 0){
  298.         cp = encode(cp,deltaS);
  299.         changes |= NEW_W;
  300.     }
  301.     if((deltaA = th.ack - oth->ack) != 0L){
  302.         if(deltaA > 0x0000ffff)
  303.             goto uncompressed;
  304.         cp = encode(cp,deltaA);
  305.         changes |= NEW_A;
  306.     }
  307.     if((deltaS = th.seq - oth->seq) != 0L){
  308.         if(deltaS > 0x0000ffff)
  309.             goto uncompressed;
  310.         cp = encode(cp,deltaS);
  311.         changes |= NEW_S;
  312.     }
  313.  
  314.     switch(changes){
  315.     case 0:    /* Nothing changed. If this packet contains data and the
  316.          * last one didn't, this is probably a data packet following
  317.          * an ack (normal on an interactive connection) and we send
  318.          * it compressed.  Otherwise it's probably a retransmit,
  319.          * retransmitted ack or window probe.  Send it uncompressed
  320.          * in case the other side missed the compressed version.
  321.          */
  322.         if(iph.length != cs->cs_ip.length && cs->cs_ip.length == hlen)
  323.             break;
  324.         goto uncompressed;
  325.     case SPECIAL_I:
  326.     case SPECIAL_D:
  327.         /* actual changes match one of our special case encodings --
  328.          * send packet uncompressed.
  329.          */
  330.         goto uncompressed;
  331.     case NEW_S|NEW_A:
  332.         if(deltaS == deltaA &&
  333.             deltaS == cs->cs_ip.length - hlen){
  334.             /* special case for echoed terminal traffic */
  335.             changes = SPECIAL_I;
  336.             cp = new_seq;
  337.         }
  338.         break;
  339.     case NEW_S:
  340.         if(deltaS == cs->cs_ip.length - hlen){
  341.             /* special case for data xfer */
  342.             changes = SPECIAL_D;
  343.             cp = new_seq;
  344.         }
  345.         break;
  346.     }
  347.     deltaS = iph.id - cs->cs_ip.id;
  348.     if(deltaS != 1){
  349.         cp = encode(cp,deltaS);
  350.         changes |= NEW_I;
  351.     }
  352.     if(th.flags.psh)
  353.         changes |= TCP_PUSH_BIT;
  354.     /* Grab the cksum before we overwrite it below.  Then update our
  355.      * state with this packet's header.
  356.      */
  357.     deltaA = th.checksum;
  358.     ASSIGN(cs->cs_ip,iph);
  359.     ASSIGN(cs->cs_tcp,th);
  360.     /* We want to use the original packet as our compressed packet.
  361.      * (cp - new_seq) is the number of bytes we need for compressed
  362.      * sequence numbers.  In addition we need one byte for the change
  363.      * mask, one for the connection id and two for the tcp checksum.
  364.      * So, (cp - new_seq) + 4 bytes of header are needed.
  365.      */
  366.     deltaS = cp - new_seq;
  367.     if(compress_cid == 0 || comp->xmit_current != cs->this){
  368.         bp = *bpp = pushdown(*bpp,deltaS + 4);
  369.         cp = bp->data;
  370.         *cp++ = changes | NEW_C;
  371.         *cp++ = cs->this;
  372.         comp->xmit_current = cs->this;
  373.     } else {
  374.         bp = *bpp = pushdown(*bpp,deltaS + 3);
  375.         cp = bp->data;
  376.         *cp++ = changes;
  377.     }
  378.     cp = put16(cp,(int16)deltaA);    /* Write TCP checksum */
  379.     memcpy(cp,new_seq, (size_t) deltaS);    /* Write list of deltas */
  380.     comp->sls_o_compressed++;
  381.     return SL_TYPE_COMPRESSED_TCP;
  382.  
  383.     /* Update connection state cs & send uncompressed packet (i.e.,
  384.      * a regular ip/tcp packet but with the 'conversation id' we hope
  385.      * to use on future compressed packets in the protocol field).
  386.      */
  387. uncompressed:
  388.     iph.protocol = cs->this;
  389.     ASSIGN(cs->cs_ip,iph);
  390.     ASSIGN(cs->cs_tcp,th);
  391.     comp->xmit_current = cs->this;
  392.     comp->sls_o_uncompressed++;
  393.     *bpp = htontcp(&th,*bpp,NULLHEADER);
  394.     *bpp = htonip(&iph,*bpp,IP_CS_OLD);
  395.     return SL_TYPE_UNCOMPRESSED_TCP;
  396. }
  397.  
  398.  
  399. int
  400. slhc_uncompress(comp, bpp)
  401. struct slcompress *comp;
  402. struct mbuf **bpp;
  403. {
  404.     register int changes;
  405.     long x;
  406.     register struct tcp *thp;
  407.     register struct cstate *cs;
  408.     int len;
  409.  
  410.     /* We've got a compressed packet; read the change byte */
  411.     comp->sls_i_compressed++;
  412.     if(len_p(*bpp) < 3){
  413.         comp->sls_i_error++;
  414.         return 0;
  415.     }
  416.     changes = PULLCHAR(bpp);    /* "Can't fail" */
  417.     if(changes & NEW_C){
  418.         /* Make sure the state index is in range, then grab the state.
  419.          * If we have a good state index, clear the 'discard' flag.
  420.          */
  421.         x = PULLCHAR(bpp);    /* Read conn index */
  422.         if(x < 0 || x > comp->rslot_limit)
  423.             goto bad;
  424.  
  425.         comp->flags &=~ SLF_TOSS;
  426.         comp->recv_current = x;
  427.     } else {
  428.         /* this packet has an implicit state index.  If we've
  429.          * had a line error since the last time we got an
  430.          * explicit state index, we have to toss the packet. */
  431.         if(comp->flags & SLF_TOSS){
  432.             comp->sls_i_tossed++;
  433.             return 0;
  434.         }
  435.     }
  436.     cs = &comp->rstate[comp->recv_current];
  437.     thp = &cs->cs_tcp;
  438.  
  439.     if((x = pull16(bpp)) == -1)    /* Read the TCP checksum */
  440.         goto bad;
  441.     thp->checksum = x;
  442.  
  443.     thp->flags.psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
  444.  
  445.     switch(changes & SPECIALS_MASK){
  446.     case SPECIAL_I:        /* Echoed terminal traffic */
  447.         {
  448.         register int16 i;
  449.         i = cs->cs_ip.length;
  450.         i -= (cs->cs_ip.optlen + IPLEN + TCPLEN);
  451.         thp->ack += i;
  452.         thp->seq += i;
  453.         }
  454.         break;
  455.  
  456.     case SPECIAL_D:            /* Unidirectional data */
  457.         thp->seq += cs->cs_ip.length - (cs->cs_ip.optlen +IPLEN + TCPLEN);
  458.         break;
  459.  
  460.     default:
  461.         if(changes & NEW_U){
  462.             thp->flags.urg = 1;
  463.             if((x = decode(bpp)) == -1)
  464.                 goto bad;
  465.             thp->up = x;
  466.         } else
  467.             thp->flags.urg = 0;
  468.         if(changes & NEW_W){
  469.             if((x = decode(bpp)) == -1)
  470.                 goto bad;
  471.             thp->wnd += x;
  472.         }
  473.         if(changes & NEW_A){
  474.             if((x = decode(bpp)) == -1)
  475.                 goto bad;
  476.             thp->ack += x;
  477.         }
  478.         if(changes & NEW_S){
  479.             if((x = decode(bpp)) == -1)
  480.                 goto bad;
  481.             thp->seq += x;
  482.         }
  483.         break;
  484.     }
  485.     if(changes & NEW_I){
  486.         if((x = decode(bpp)) == -1)
  487.             goto bad;
  488.         cs->cs_ip.id += x;
  489.     } else
  490.         cs->cs_ip.id++;
  491.  
  492.     /*
  493.      * At this point, bpp points to the first byte of data in the
  494.      * packet.  Put the reconstructed TCP and IP headers back on the
  495.      * packet.  Recalculate IP checksum (but not TCP checksum).
  496.      */
  497.     len = len_p(*bpp) + IPLEN + TCPLEN + cs->cs_ip.optlen;
  498.     cs->cs_ip.length = len;
  499.  
  500.     *bpp = htontcp(thp,*bpp,NULLHEADER);
  501.     *bpp = htonip(&cs->cs_ip,*bpp,IP_CS_NEW);
  502.     return len;
  503. bad:
  504.     comp->sls_i_error++;
  505.     return slhc_toss( comp );
  506. }
  507.  
  508.  
  509. int
  510. slhc_remember(comp, bpp)
  511. struct slcompress *comp;
  512. struct mbuf **bpp;
  513. {
  514.     register struct cstate *cs;
  515.     struct ip iph;
  516.     struct tcp th;
  517.  
  518.     /* Extract IP and TCP headers and verify conn ID */
  519.     ntohip(&iph,bpp);
  520.     ntohtcp(&th,bpp);
  521.     if(uchar(iph.protocol) > comp->rslot_limit) {
  522.         comp->sls_i_error++;
  523.         return slhc_toss(comp);
  524.     }
  525.  
  526.     /* Update local state */
  527.     cs = &comp->rstate[comp->recv_current = uchar(iph.protocol)];
  528.     comp->flags &=~ SLF_TOSS;
  529.     iph.protocol = TCP_PTCL;
  530.     ASSIGN(cs->cs_ip,iph);
  531.     ASSIGN(cs->cs_tcp,th);
  532.  
  533.     /* Put headers back on packet
  534.      * Neither header checksum is recalculated
  535.      */
  536.     *bpp = htontcp(&th,*bpp,NULLHEADER);
  537.     *bpp = htonip(&iph,*bpp,IP_CS_OLD);
  538.     comp->sls_i_uncompressed++;
  539.     return len_p(*bpp);
  540. }
  541.  
  542.  
  543. int
  544. slhc_toss(comp)
  545. struct slcompress *comp;
  546. {
  547.     if ( comp == NULLSLCOMPR )
  548.         return 0;
  549.  
  550.     comp->flags |= SLF_TOSS;
  551.     return 0;
  552. }
  553.  
  554.  
  555. void slhc_i_status(comp)
  556. struct slcompress *comp;
  557. {
  558.     if (comp != NULLSLCOMPR) {
  559.         tprintf("\t%10ld Cmp,"
  560.             " %10ld Uncmp,"
  561.             " %10ld Bad, "
  562.             " %10ld Tossed\n",
  563.             comp->sls_i_compressed,
  564.             comp->sls_i_uncompressed,
  565.             comp->sls_i_error,
  566.             comp->sls_i_tossed);
  567.     }
  568. }
  569.  
  570.  
  571. void slhc_o_status(comp)
  572. struct slcompress *comp;
  573. {
  574.     if (comp != NULLSLCOMPR) {
  575.         tprintf("\t%10ld Cmp,"
  576.             " %10ld Uncmp,"
  577.             " %10ld AsIs,"
  578.             " %10ld NotTCP\n",
  579.             comp->sls_o_compressed,
  580.             comp->sls_o_uncompressed,
  581.             comp->sls_o_tcp,
  582.             comp->sls_o_nontcp);
  583.         tprintf("\t%10ld Searches,"
  584.             " %10ld Misses\n",
  585.             comp->sls_o_searches,
  586.             comp->sls_o_misses);
  587.     }
  588. }
  589.  
  590.  
  591.